{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c314393b",
   "metadata": {},
   "source": [
    "### Relevant Python 3.8 Changes"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cbe45497",
   "metadata": {},
   "source": [
    "The release of Python 3.8 has brought some new features.\n",
    "\n",
    "This is a summary of the ones _I_ deemed relevant to this course, and does **not** include all the changes!\n",
    "\n",
    "For full release details, see [here](https://docs.python.org/3/whatsnew/3.8.html)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7089277d",
   "metadata": {},
   "source": [
    "#### Positional Only Parameters"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61154c87",
   "metadata": {},
   "source": [
    "It is now possible to define **positional-only** parameters for Python functions."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "63037bda",
   "metadata": {},
   "source": [
    "As we saw earlier in this course, when you define positional parameters in a function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "13c1ba59",
   "metadata": {},
   "outputs": [],
   "source": [
    "def my_func(a, b):\n",
    "    return a + b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a4928079",
   "metadata": {},
   "source": [
    "the user is free to pass arguments either positionally:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "0bb6fea5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_func(1, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "be1cfa90",
   "metadata": {},
   "source": [
    "or, as named arguments:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "cb04e54f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_func(b=2, a=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "617077dd",
   "metadata": {},
   "source": [
    "Some functions in Python's built-ins ared defined in such a way that certain parameters can **only** be passed positionally, for example the `print` function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "77c6fa8a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on built-in function print in module builtins:\n",
      "\n",
      "print(...)\n",
      "    print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n",
      "    \n",
      "    Prints the values to a stream, or to sys.stdout by default.\n",
      "    Optional keyword arguments:\n",
      "    file:  a file-like object (stream); defaults to the current sys.stdout.\n",
      "    sep:   string inserted between values, default a space.\n",
      "    end:   string appended after the last value, default a newline.\n",
      "    flush: whether to forcibly flush the stream.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(print)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "248c2581",
   "metadata": {},
   "source": [
    "That `value` cannot be passed by name:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "529c5dac",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "'value' is an invalid keyword argument for print()\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    print(value=\"hello\")\n",
    "except TypeError as ex:\n",
    "    print(ex)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4baed67",
   "metadata": {},
   "source": [
    "Instead, the parameter **must** be passed positionally:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "8c147557",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello\n"
     ]
    }
   ],
   "source": [
    "print(\"hello\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2840374",
   "metadata": {},
   "source": [
    "Until Python 3.8, it was not possible to reproduce such behavior with user-defined functions."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "158674a9",
   "metadata": {},
   "source": [
    "Now you can, by using the slash character(`/`). Parameters defined **before** the `/` become **positional-only** parameters:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "aeebd0f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "def my_func(a, b, /):\n",
    "    return a + b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "858727b6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_func(1, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "4f73c741",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "my_func() got some positional-only arguments passed as keyword arguments: 'a, b'\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    my_func(a=1, b=2)\n",
    "except TypeError as ex:\n",
    "    print(ex)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f5967700",
   "metadata": {},
   "source": [
    "You can of course mix this along with the special parameters `*` and `**`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "d17254b3",
   "metadata": {},
   "outputs": [],
   "source": [
    "def my_func(a, b, /, *, c):\n",
    "    print(a + b + c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "b35d884e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "13\n"
     ]
    }
   ],
   "source": [
    "my_func(1, 2, c=10)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "573aca30",
   "metadata": {},
   "source": [
    "#### f-string Enhancements"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "89ee51f9",
   "metadata": {},
   "source": [
    "Often we use f-strings to interpolate the name of a variable and it's value:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "f79a630b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a=hello, b=world\n"
     ]
    }
   ],
   "source": [
    "a, b = \"hello\", \"world\"\n",
    "print(f\"a={a}, b={b}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24747607",
   "metadata": {},
   "source": [
    "Python 3.8 provides a shortcut way of doing the same thing:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "3ae5c0c4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a='hello', b='world'\n"
     ]
    }
   ],
   "source": [
    "print(f\"{a=}, {b=}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "141558b4",
   "metadata": {},
   "source": [
    "You can even use [format specifiers](https://docs.python.org/3/library/string.html#formatspec)\n",
    "to further customize the output:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "e75716ac",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a=hello, b=world\n"
     ]
    }
   ],
   "source": [
    "print(f\"{a=:s}, {b=:s}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4dcdc5d0",
   "metadata": {},
   "source": [
    "Or when dealing with other types:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "362bf6b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "from datetime import datetime\n",
    "from math import pi"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "c91b7536",
   "metadata": {},
   "outputs": [],
   "source": [
    "d = datetime.utcnow()\n",
    "e = pi"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "545c77a2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "d=datetime.datetime(2022, 3, 20, 6, 1, 13, 990493), e=3.141592653589793\n"
     ]
    }
   ],
   "source": [
    "print(f\"{d=}, {e=}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4637ecd5",
   "metadata": {},
   "source": [
    "And applying some format specifiers:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "75c4052b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "d=2022-03-20 06:01:13, e=3.142\n"
     ]
    }
   ],
   "source": [
    "print(f\"{d=:%Y-%m-%d %H:%M:%S}, {e=:.3f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "076773ed",
   "metadata": {},
   "source": [
    "It will even display the text of an expression if you use one in your f-string:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "0ce46ace",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 + 2=3, ' '.join(sentence)='Python rocks!'\n"
     ]
    }
   ],
   "source": [
    "sentence = [\"Python\", \"rocks!\"]\n",
    "print(f\"{1 + 2=}, {' '.join(sentence)=}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e3783ea",
   "metadata": {},
   "source": [
    "#### The `as_integer_ratio()` Method"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15ccd5ad",
   "metadata": {},
   "source": [
    "The types `bool`, `int` and `Fraction` now all implement an `as_integer_ratio()` method which returns a tuple consisting of the numerator and denominator. Remember that `Decimal` and `float` already implement the same method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "8313fc80",
   "metadata": {},
   "outputs": [],
   "source": [
    "from fractions import Fraction"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "7a1ae84a",
   "metadata": {},
   "outputs": [],
   "source": [
    "f = Fraction(2, 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "a94fc3ed",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 3)"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f.as_integer_ratio()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "f32942ec",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(12, 1)"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = 12\n",
    "a.as_integer_ratio()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "06980a8c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 1)"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "flag = True\n",
    "flag.as_integer_ratio()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3091e0e",
   "metadata": {},
   "source": [
    "The advantage of this is mainly for polymorphism (or duck-typing), where you can now use `as_integer_ratio` irrespective of whether the variable is a `bool`, an `int`, a `float`, a `Decimal` or a `Fraction`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "8a362bf8",
   "metadata": {},
   "outputs": [],
   "source": [
    "from decimal import Decimal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "ab4a78aa",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(33, 100)"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Decimal(\"0.33\").as_integer_ratio()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "5ee29a47",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(7070651414971679, 2251799813685248)"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(3.14).as_integer_ratio()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "df1e85de",
   "metadata": {},
   "source": [
    "#### The `lru_cache` decorator"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "226efb47",
   "metadata": {},
   "source": [
    "As we saw in this course, we can use the `lru_cache` decorator to appky an LRU cache to our functions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "20501526",
   "metadata": {},
   "outputs": [],
   "source": [
    "from functools import lru_cache"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "07d699f0",
   "metadata": {},
   "outputs": [],
   "source": [
    "@lru_cache(maxsize=3)\n",
    "def fib(n):\n",
    "    if n <=2 :\n",
    "        return 1\n",
    "    else:\n",
    "        return fib(n-1) + fib(n-2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "45a0cd5a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "354224848179261915075"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fib(100)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fea747d4",
   "metadata": {},
   "source": [
    "If we don't specify `maxsize`, it will default to `128`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "447be276",
   "metadata": {},
   "outputs": [],
   "source": [
    "@lru_cache()\n",
    "def fib(n):\n",
    "    if n <=2 :\n",
    "        return 1\n",
    "    else:\n",
    "        return fib(n-1) + fib(n-2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "53fee4fd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "354224848179261915075"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fib(100)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5cf8e33a",
   "metadata": {},
   "source": [
    "The change made to this decorator in Python 3.8 allows us not to use those empty parentheses:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "4185658b",
   "metadata": {},
   "outputs": [],
   "source": [
    "@lru_cache\n",
    "def fib(n):\n",
    "    if n <=2 :\n",
    "        return 1\n",
    "    else:\n",
    "        return fib(n-1) + fib(n-2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "76a9aed5",
   "metadata": {},
   "source": [
    "#### `math` Module"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0af410c0",
   "metadata": {},
   "source": [
    "Many examples I use throughout this course calculate the Euclidean distance between two points:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "b511addc",
   "metadata": {},
   "outputs": [],
   "source": [
    "import math"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "0b66c006",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.4142135623730951\n"
     ]
    }
   ],
   "source": [
    "a = (0, 0)\n",
    "b = (1, 1)\n",
    "\n",
    "dist = math.sqrt((b[0] - a[1]) ** 2 + (b[1] - a[1]) ** 2)\n",
    "print(dist)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d276589d",
   "metadata": {},
   "source": [
    "Now, it's much easier using the `dist()` function the `math` module:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "1f774757",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.4142135623730951"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "math.dist(a, b)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13afb1f1",
   "metadata": {},
   "source": [
    "#### The `namedtuple` Implementation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a22a839e",
   "metadata": {},
   "source": [
    "Actually these changes were added to Python 3.7, but since I don't have a separate lecture for Python 3.7 changes (most did not apply to this course), here it is."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a579fbb",
   "metadata": {},
   "source": [
    "The `_source` attribute was **removed**. There quite a discussion on this, and the the core dev who implemented and supported this essentially gave up trying to keep this in - it was deemed to cause too much \"overhead\". So, sadly (wearing my teacher's hat), it is gone. It is no more. It's not pining, it's just dead. :-)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d5e3bfd0",
   "metadata": {},
   "source": [
    "The method I showed you for defining defaults for named tuples still works, and could still be used, but Python 3.7 added the `defaults` parameter to the named tuple definition."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "3538a2c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import namedtuple"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "024bcc51",
   "metadata": {},
   "outputs": [],
   "source": [
    "NT = namedtuple(\"NT\", \"a b c\", defaults=(10, 20, 30))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "9dd8a877",
   "metadata": {},
   "outputs": [],
   "source": [
    "nt = NT()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "db7f47e7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "NT(a=10, b=20, c=30)"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb0bc811",
   "metadata": {},
   "source": [
    "You don't have to specify defaults for everything, but if you do not, be aware that defaults will be applied from **right** to **left**. Which makes sense given that in Python non-defaulted parameters must be defined **before** defaulted parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "8f6ad2ca",
   "metadata": {},
   "outputs": [],
   "source": [
    "NT = namedtuple(\"NT\", \"a b c\", defaults = (20, 30))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "bc9d5345",
   "metadata": {},
   "outputs": [],
   "source": [
    "nt = NT(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "eceeaec2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "NT(a=10, b=20, c=30)"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64188b1c",
   "metadata": {},
   "source": [
    "Note that with this way of specifying defaults you can easily define the same default for all items in the named tuple using the `*` operator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "8b36d7c3",
   "metadata": {},
   "outputs": [],
   "source": [
    "NT = namedtuple(\"NT\", \"a b c d e f\", defaults=(\"xyz\",) * 6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "1d448806",
   "metadata": {},
   "outputs": [],
   "source": [
    "nt = NT()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "b16758de",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "NT(a='xyz', b='xyz', c='xyz', d='xyz', e='xyz', f='xyz')"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bbcbaf53",
   "metadata": {},
   "source": [
    "Just be careful if you use a **mutable** type to do this!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "91b28470",
   "metadata": {},
   "outputs": [],
   "source": [
    "NT = namedtuple(\"NT\", \"a b c\", defaults = ([],) * 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "cad11f5c",
   "metadata": {},
   "outputs": [],
   "source": [
    "nt = NT()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "0cb7ceac",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "NT(a=[], b=[], c=[])"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "466d9520",
   "metadata": {},
   "outputs": [],
   "source": [
    "nt.a.append(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "74826099",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[10]"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nt.a"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d00b122",
   "metadata": {},
   "source": [
    "But watch this!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "4759e6f4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "NT(a=[10], b=[10], c=[10])"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "972c666e",
   "metadata": {},
   "source": [
    "I hope you understand what happened here without me telling you!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6590d73a",
   "metadata": {},
   "source": [
    "The **same** list object was re-used 3 times in the defaults."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "720e13bb",
   "metadata": {},
   "source": [
    "You can easily recover your defaults using the `_field_defaults` method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "586498ae",
   "metadata": {},
   "outputs": [],
   "source": [
    "NT = namedtuple(\"NT\", \"a, b, c\", defaults=(1, 2, 3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "id": "20f4012e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'a': 1, 'b': 2, 'c': 3}"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "NT._field_defaults"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70c3a717",
   "metadata": {},
   "source": [
    "One change of note in Python 3.8, the `_as_dict()` method now returns a standard dictionary (key ordered in the same way as the named tuple). Prior to this version, it would return an `OrderedDict` since standard Python dictionaries did not guarantee any specific key order, but since they now do, there's no need to use the `DefaultDict` anymore."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ecb5b3a0",
   "metadata": {},
   "source": [
    "#### Other Things"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9d8ec24b",
   "metadata": {},
   "source": [
    "These are few other odds and ends that you might find of interest:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3602bbab",
   "metadata": {},
   "source": [
    "The built-in `reversed` function now works with dictionary views:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "4f68e46b",
   "metadata": {},
   "outputs": [],
   "source": [
    "d = {'a': 1, 'b': 2}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "83fc356d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['a', 'b']"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(d.keys())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "10648abb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['b', 'a']"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(reversed(d.keys()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "id": "4a4885c3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[2, 1]"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(reversed(d.values()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "13785eb9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('b', 2), ('a', 1)]"
      ]
     },
     "execution_count": 59,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(reversed(d.items()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e14d5c14",
   "metadata": {},
   "source": [
    "The `continue` statement was not permitted in the `finally` clause of a loop, but is now supported."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fe507127",
   "metadata": {},
   "source": [
    "Earlier in Part 1, we discussed string interning, as well as how a small selection of integers are essentially \"cached\" by Python and re-used whenever the literal is encountered. This meant we could use `is` instead of `==` in some cases, and that helped us get a clearer understanding of what's going on. **BUT**, as we also discussed, you should **not**, in practice, use `is` for comparing objects such as integers, strings, etc (usually we are more interested in whether is the same value, rather than the identical object) - the fact that this works is an implementation detail and not guaranteed to work the same way from one Python version to another.\n",
    "\n",
    "Although linters will usually catch those kinds of issues, not everyone uses a Python linter - so Python 3.8 will now emit a **warning** if you compare variables of certain types with literal constants!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "id": "e659956e",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n",
      "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n",
      "/var/folders/kg/20pf1n8j6pjg09bwxz6cgw0r0000gn/T/ipykernel_33116/3780910533.py:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n",
      "  a is 1\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = 1\n",
    "a is 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "id": "d970904d",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n",
      "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n",
      "/var/folders/kg/20pf1n8j6pjg09bwxz6cgw0r0000gn/T/ipykernel_33116/4272392880.py:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n",
      "  a is 'hello'\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = 'hello'\n",
    "a is 'hello'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "01c01a49",
   "metadata": {},
   "source": [
    "But we do not get a warning in a situation such as this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "id": "c0d535bc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 62,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = [1, 2, 3]\n",
    "\n",
    "a is [1, 2, 3]"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
